package cn.iocoder.yudao.module.statistics.service.shareprofit;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.statistics.controller.admin.shareprofit.vo.*;
import cn.iocoder.yudao.module.statistics.controller.app.shareprofit.vo.AppStoreTradeShareProfitPageReqVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.shareprofit.StoreTradeShareProfitDO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.dal.mysql.shareprofit.StoreTradeShareProfitMapper;
import cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeStatisticsMapper;
import cn.iocoder.yudao.module.system.api.partner.PartnerApi;
import cn.iocoder.yudao.module.system.api.partner.dto.PartnerReqDTO;
import cn.iocoder.yudao.module.system.api.partner.dto.PartnerRespDTO;
import cn.iocoder.yudao.module.system.api.partner.dto.PartnerSendReqDTO;
import cn.iocoder.yudao.module.system.api.partnerstore.PartnerStoreApi;
import cn.iocoder.yudao.module.system.api.partnerstore.dto.PartnerStoreReqDTO;
import cn.iocoder.yudao.module.system.api.partnerstore.dto.PartnerStoreRespDTO;
import cn.iocoder.yudao.module.system.enums.partner.PartnerTypeEnum;
import cn.iocoder.yudao.module.trade.api.store.StoreApi;
import cn.iocoder.yudao.module.trade.api.store.dto.StoreReqDTO;
import cn.iocoder.yudao.module.trade.api.store.dto.StoreRespDTO;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.statistics.enums.ErrorCodeConstants.STORE_TRADE_SHARE_PROFIT_NOT_EXISTS;

/**
 * 门店交易合伙人分润 Service 实现类
 *
 * @author 万民匠心
 */
@Service
@Validated
@Slf4j
public class StoreTradeShareProfitServiceImpl implements StoreTradeShareProfitService {

    @Resource
    private StoreTradeShareProfitMapper storeTradeShareProfitMapper;

    @Resource
    private TradeStatisticsMapper tradeStatisticsMapper;

    @Resource
    private PartnerStoreApi partnerStoreApi;

    @Resource
    private PartnerApi partnerApi;
    @Resource
    private StoreApi storeApi;

    @Resource
    private MemberUserApi memberUserApi;

    @Override
    public Long createStoreTradeShareProfit(StoreTradeShareProfitSaveReqVO createReqVO) {
        // 插入
        StoreTradeShareProfitDO storeTradeShareProfit = BeanUtils.toBean(createReqVO, StoreTradeShareProfitDO.class);
        storeTradeShareProfitMapper.insert(storeTradeShareProfit);
        // 返回
        return storeTradeShareProfit.getId();
    }

    @Override
    public void updateStoreTradeShareProfit(StoreTradeShareProfitSaveReqVO updateReqVO) {
        // 校验存在
        validateStoreTradeShareProfitExists(updateReqVO.getId());
        // 更新
        StoreTradeShareProfitDO updateObj = BeanUtils.toBean(updateReqVO, StoreTradeShareProfitDO.class);
        storeTradeShareProfitMapper.updateById(updateObj);
    }

    @Override
    public void deleteStoreTradeShareProfit(Long id) {
        // 校验存在
        validateStoreTradeShareProfitExists(id);
        // 删除
        storeTradeShareProfitMapper.deleteById(id);
    }

    private void validateStoreTradeShareProfitExists(Long id) {
        if (storeTradeShareProfitMapper.selectById(id) == null) {
            throw exception(STORE_TRADE_SHARE_PROFIT_NOT_EXISTS);
        }
    }

    @Override
    public StoreTradeShareProfitDO getStoreTradeShareProfit(Long id) {
        return storeTradeShareProfitMapper.selectById(id);
    }

    @Override
    public PageResult<StoreTradeShareProfitDO> getStoreTradeShareProfitPage(StoreTradeShareProfitPageReqVO pageReqVO) {
        if (!StringUtils.isAllBlank(pageReqVO.getPartnerName(), pageReqVO.getPhone())) {
            PartnerReqDTO reqVO = PartnerReqDTO.builder().build().setName(pageReqVO.getPartnerName()).setPhone(pageReqVO.getPhone());
            List<PartnerRespDTO> partnerList = partnerApi.getListApi(reqVO);
            if (CollectionUtils.isAnyEmpty(partnerList)) {
                return PageResult.empty();
            }
            List<Long> partnerIds = partnerList.stream().map(PartnerRespDTO::getId).collect(Collectors.toList());
            pageReqVO.setPartnerIds(partnerIds);
        }
        if (Objects.nonNull(pageReqVO.getStatus())) {
            StoreReqDTO reqDTO = new StoreReqDTO();
            reqDTO.setStatus(pageReqVO.getStatus());
            List<StoreRespDTO> storeList = storeApi.storeListApi(reqDTO);
            if (CollectionUtils.isAnyEmpty(storeList)) {
                return PageResult.empty();
            }
            List<Long> storeIds = storeList.stream().map(StoreRespDTO::getId).collect(Collectors.toList());
            pageReqVO.setStoreIds(storeIds);
        }
        return storeTradeShareProfitMapper.selectPage(pageReqVO);
    }

    @Override
    public String shareProfit(Integer days) {
        LocalDate today = LocalDate.now();
        IntStream.rangeClosed(-days, -1)
                .mapToObj(day -> shareProfit(today.plusDays(day)))
                .sorted()
                .collect(Collectors.joining("\n"));

        //查询所有的分润，求和
        List<StoreTradeShareProfitDO> shareProfitDOList = storeTradeShareProfitMapper.selectPartnerShareProfit(new StoreTradeShareProfitDO());
        List<PartnerSendReqDTO> updatePartnerList = Lists.newArrayList();
        if (!CollectionUtils.isAnyEmpty(shareProfitDOList)) {
            shareProfitDOList.forEach(shareDo -> {
                Long totalCashoutAmount = shareDo.getShareProfitPayPrice() - shareDo.getShareProfitRefundPrice();//实际支付-退款
                PartnerSendReqDTO sendReqDTO = new PartnerSendReqDTO();
                sendReqDTO.setPartnerId(shareDo.getPartnerId());
                sendReqDTO.setAmount(totalCashoutAmount);
                sendReqDTO.setAmountType(1);
                updatePartnerList.add(sendReqDTO);
            });
            partnerApi.updatePartnerAmount(updatePartnerList);
        }

        return null;
    }

    /**
     * 计算分润
     *
     * @param currentDate 需要统计的日期
     * @return 统计结果
     */
    private String shareProfit(LocalDate currentDate) {

        PartnerStoreReqDTO reqDTO = new PartnerStoreReqDTO();
        reqDTO.setShareProfitDate(currentDate);
        //获取需要分润的店铺&合伙人
        List<PartnerStoreRespDTO> partnerStoreList = partnerStoreApi.getPartnerStoreList(reqDTO);
        List<Long> storeIds = partnerStoreList.stream().map(PartnerStoreRespDTO::getStoreId).collect(Collectors.toList());
        List<Long> partnerIds = partnerStoreList.stream().map(PartnerStoreRespDTO::getPartnerId).distinct().collect(Collectors.toList());
        List<PartnerStoreRespDTO> referList = partnerStoreList.stream().filter(item -> partnerIds.contains(item.getReferrerType())).collect(Collectors.toList());//获取所有的推荐人
        LocalDateTime startTime = currentDate.atStartOfDay();//开始时间
        LocalDateTime endTime = LocalDateTimeUtil.endOfDay(currentDate.atStartOfDay());//结束时间

        List<StoreTradeShareProfitDO> todayList = storeTradeShareProfitMapper.selectList(new LambdaQueryWrapperX<StoreTradeShareProfitDO>()
                .eq(StoreTradeShareProfitDO::getReportDate, currentDate));

        StoreTradeShareProfitDO reqDO = new StoreTradeShareProfitDO().setReportDate(currentDate);
        List<StoreTradeShareProfitDO> hasSharedList = storeTradeShareProfitMapper.selectHasShareProfitList(reqDO);

        Map<String, StoreTradeShareProfitDO> todayMap = !CollectionUtils.isAnyEmpty(todayList) ?
                todayList.stream().collect(Collectors.toMap(profit -> profit.getPartnerType()+"_"+profit.getPartnerId()+"_"+ profit.getStoreId(), profit -> profit))
                : Maps.newHashMap();

        Map<String, StoreTradeShareProfitDO> hasSharedMap = !CollectionUtils.isAnyEmpty(hasSharedList) ?
                hasSharedList.stream().collect(Collectors.toMap(profit -> profit.getPartnerType()+"_"+profit.getPartnerId()+"_"+ profit.getStoreId(), profit -> profit))
                : Maps.newHashMap();

        List<TradeStatisticsDO> tradeList = tradeStatisticsMapper.selectList(new LambdaQueryWrapperX<TradeStatisticsDO>()
                .inIfPresent(TradeStatisticsDO::getStoreId, storeIds)
                .geIfPresent(TradeStatisticsDO::getTime, startTime)
                .leIfPresent(TradeStatisticsDO::getTime, endTime));
        StopWatch stopWatch = new StopWatch(currentDate.toString());
        stopWatch.start("计算分润");
        List<StoreTradeShareProfitDO> shareProfitList = Lists.newArrayList();
        if (!CollectionUtils.isAnyEmpty(tradeList)) {
            tradeList.forEach(trade -> {
                partnerStoreList.forEach(partner -> {
                    if (trade.getStoreId().equals(partner.getStoreId())) {
                        Integer partnerType = partner.getPartnerType();
                        Long partnerId = partner.getPartnerId();
                        Long storeId = partner.getStoreId();
                        String validateKey = partnerType + "_" + partnerId + "_" + storeId;
                        if (todayMap.containsKey(validateKey)) {
                            log.error("【{}】-【{}】-【{}】-对应店铺【{}】数据已存在，如果需要重新统计，请先删除对应的数据.",
                                    currentDate, PartnerTypeEnum.getNameByType(partnerType), partnerId, storeId);
                            return;
                        }
                        StoreTradeShareProfitDO shareProfitDO = new StoreTradeShareProfitDO();
                        shareProfitDO.setReportDate(currentDate);
                        shareProfitDO.setPartnerType(partnerType);

                        BigDecimal shareProfitRate = partner.getPaybackBeforeProfitRate();//回本前比率
                        StoreTradeShareProfitDO hasShareProfit = hasSharedMap.get(validateKey);
                        if (Objects.nonNull(hasShareProfit) && hasShareProfit.getShareProfitPayPrice() > partner.getPartnerCost()) {
                            shareProfitRate = partner.getPaybackAfterProfitRate();//回本前比率
                        }

                        shareProfitDO.setShareProfitRate(shareProfitRate);//设置分润比率

                        shareProfitDO.setOrderPayPrice(trade.getOrderPayPrice().longValue());
                        BigDecimal shareProfitPayPrice = shareProfitDO.getShareProfitRate().divide(new BigDecimal(100L)).multiply(new BigDecimal(shareProfitDO.getOrderPayPrice().longValue()));
                        shareProfitDO.setShareProfitPayPrice(shareProfitPayPrice.longValue());//分润

                        shareProfitDO.setRefundPrice(trade.getAfterSaleRefundPrice().longValue());
                        BigDecimal shareProfitRefundPrice = shareProfitDO.getShareProfitRate().divide(new BigDecimal(100L)).multiply(new BigDecimal(shareProfitDO.getRefundPrice().longValue()));
                        shareProfitDO.setShareProfitRefundPrice(shareProfitRefundPrice.longValue());

                        shareProfitDO.setStoreId(storeId);
                        shareProfitDO.setStoreName(trade.getStoreName());
                        shareProfitDO.setPartnerId(partnerId);
                        shareProfitList.add(shareProfitDO);
                    }
                });

                referList.forEach(partner -> {
                    if (trade.getStoreId() == partner.getStoreId()) {
                        Integer partnerType = PartnerTypeEnum.REFER.getType();
                        Long partnerId = partner.getReferrerType();
                        Long storeId = partner.getStoreId();

                        String validateKey = partnerType + "_" + partnerId + "_" + storeId;
                        if (todayMap.containsKey(validateKey)) {
                            log.error("【{}】-【{}】-【{}】-对应店铺【{}】数据已存在，如果需要重新统计，请先删除对应的数据.",
                                    currentDate, PartnerTypeEnum.getNameByType(partnerType), partnerId, storeId);
                            return;
                        }
                        StoreTradeShareProfitDO shareProfitDO = new StoreTradeShareProfitDO();
                        shareProfitDO.setReportDate(currentDate);
                        shareProfitDO.setPartnerType(PartnerTypeEnum.REFER.getType());

                        BigDecimal shareProfitRate = partner.getReferrerProfitRate();

                        shareProfitDO.setShareProfitRate(shareProfitRate);//设置分润比率

                        shareProfitDO.setOrderPayPrice(trade.getOrderPayPrice().longValue());
                        BigDecimal shareProfitPayPrice = shareProfitDO.getShareProfitRate().divide(new BigDecimal(100L)).multiply(new BigDecimal(shareProfitDO.getOrderPayPrice().longValue()));
                        shareProfitDO.setShareProfitPayPrice(shareProfitPayPrice.longValue());//分润

                        shareProfitDO.setRefundPrice(trade.getAfterSaleRefundPrice().longValue());
                        BigDecimal shareProfitRefundPrice = shareProfitDO.getShareProfitRate().divide(new BigDecimal(100L)).multiply(new BigDecimal(shareProfitDO.getRefundPrice().longValue()));
                        shareProfitDO.setShareProfitRefundPrice(shareProfitRefundPrice.longValue());

                        shareProfitDO.setStoreId(storeId);
                        shareProfitDO.setStoreName(trade.getStoreName());
                        shareProfitDO.setPartnerId(partnerId);
                        shareProfitList.add(shareProfitDO);
                    }
                });
            });
        }

        if (!CollectionUtils.isAnyEmpty(shareProfitList)) {
            storeTradeShareProfitMapper.insertBatch(shareProfitList);
        }
        stopWatch.stop();
        return stopWatch.prettyPrint();
    }

    @Override
    public Map<String, Long> queryShareProfit(Long userId) {
        Map<String, Long> dataMap = Maps.newHashMap();
        Long totalShareProfit = 0l, yesterdayShareProfit = 0l;
        MemberUserRespDTO user = memberUserApi.getUser(userId);
        if (Objects.nonNull(user)) {
            PartnerRespDTO partnerRespDTO = partnerApi.getEnabledPartnerByPhone(user.getMobile());
            if (Objects.nonNull(partnerRespDTO)) {
                StoreTradeShareProfitDO reqDO = new StoreTradeShareProfitDO();
                reqDO.setPartnerId(partnerRespDTO.getId());
                List<StoreTradeShareProfitDO> totalShareProfitList = storeTradeShareProfitMapper.selectPartnerShareProfit(reqDO);
                if (!CollectionUtils.isAnyEmpty(totalShareProfitList)) {
                    StoreTradeShareProfitDO totalDO = totalShareProfitList.get(0);
                    totalShareProfit = totalDO.getShareProfitPayPrice();
                }
                reqDO.setReportDate(LocalDate.now().minusDays(1));
                List<StoreTradeShareProfitDO> yesterdayShareProfitList = storeTradeShareProfitMapper.selectPartnerShareProfit(reqDO);
                if (!CollectionUtils.isAnyEmpty(yesterdayShareProfitList)) {
                    StoreTradeShareProfitDO yesterdayDO = yesterdayShareProfitList.get(0);//总收益
                    yesterdayShareProfit = yesterdayDO.getShareProfitPayPrice();
                }
            }
        }
        dataMap.put("totalShareProfit", totalShareProfit);//总收益
        dataMap.put("yesterdayShareProfit", yesterdayShareProfit);//总收益
        return dataMap;
    }

    public PageResult<StoreTradeShareProfitDO> queryDetailList(AppStoreTradeShareProfitPageReqVO pageReqVO) {
        MemberUserRespDTO user = memberUserApi.getUser(pageReqVO.getUserId());
        if (Objects.nonNull(user)) {
            PartnerRespDTO partnerRespDTO = partnerApi.getEnabledPartnerByPhone(user.getMobile());
            pageReqVO.setPartnerId(partnerRespDTO.getId());
            if (Objects.nonNull(partnerRespDTO)) {
                return storeTradeShareProfitMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<StoreTradeShareProfitDO>()
                        .eqIfPresent(StoreTradeShareProfitDO::getReportDate, pageReqVO.getReportDate())
                        .eqIfPresent(StoreTradeShareProfitDO::getPartnerId, pageReqVO.getPartnerId())
                        .orderByDesc(StoreTradeShareProfitDO::getReportDate));
            }
        }
        return PageResult.empty();
    }

    @Override
    public PageResult<StoreShareProfitVO> getByStorePage(StoreShareProfitPageReqVO reqVO) {
        if (StringUtils.isNotBlank(reqVO.getStartMonth())) {
            LocalDate startTime = LocalDateTimeUtils.beginOfMonth(DateUtil.parseLocalDateTime(reqVO.getStartMonth() + "-01", DateUtils.FORMAT_YEAR_MONTH_DAY)).toLocalDate();
            reqVO.setStartTime(startTime);
        }

        if (StringUtils.isNotBlank(reqVO.getEndMonth())) {
            LocalDate endTime = LocalDateTimeUtils.endOfMonth(DateUtil.parseLocalDateTime(reqVO.getEndMonth() + "-01", DateUtils.FORMAT_YEAR_MONTH_DAY)).toLocalDate();
            reqVO.setEndTime(endTime);
        }
        PageResult<StoreShareProfitVO> pageResult = new PageResult<>();
        pageResult.setList(Collections.emptyList());
        pageResult.setTotal(0l);
        Integer count = storeTradeShareProfitMapper.getByStoreCount(reqVO);
        if (count > 0) {
            reqVO.setStart((reqVO.getPageNo()-1) * reqVO.getPageSize());
            List<StoreShareProfitVO> shareProfitList = storeTradeShareProfitMapper.getByStorePage(reqVO);
            List<Long> storeIds = shareProfitList.stream().map(StoreShareProfitVO::getStoreId).collect(Collectors.toList());

            List<TradeStatisticsDO> tradeList = tradeStatisticsMapper.selectList(new LambdaQueryWrapperX<TradeStatisticsDO>()
                    .inIfPresent(TradeStatisticsDO::getStoreId, storeIds)
                    .geIfPresent(TradeStatisticsDO::getTime, reqVO.getStartTime())
                    .ltIfPresent(TradeStatisticsDO::getTime, Objects.nonNull(reqVO.getEndTime()) ?reqVO.getEndTime().plusDays(1):null));

            Function<TradeStatisticsDO, String> groupByKey = trade -> trade.getTime().toString().substring(0,7) + "_"+ trade.getStoreId();

            Map<String, List<TradeStatisticsDO>> tradeMap = tradeList.stream().collect(Collectors.groupingBy(groupByKey));

            shareProfitList.forEach(item -> {
                String tradeKey = item.getReportDate() + "_" + item.getStoreId();
                Long orderPayPrice = 0l;
                if (tradeMap.containsKey(tradeKey)) {
                    List<TradeStatisticsDO> tradeLists = tradeMap.get(tradeKey);
                    Long totalPayPrice = tradeLists.stream().mapToLong(TradeStatisticsDO::getOrderPayPrice).sum();
                    Long totalRefundPrice = tradeLists.stream().mapToLong(TradeStatisticsDO::getAfterSaleRefundPrice).sum();
                    orderPayPrice = totalPayPrice - totalRefundPrice;
                    item.setOrderPayPrice(orderPayPrice);
                }
                double shareProfitRate = item.getOrderPayPrice() == 0 ? 0 : NumberUtil.div(item.getShareProfitPayPrice().floatValue(), item.getOrderPayPrice().floatValue(), 2);
                item.setShareProfitRate(new BigDecimal(shareProfitRate).multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP));
            });
            pageResult.setList(shareProfitList);
            pageResult.setTotal(count.longValue());
        }
        return pageResult;
    }



    @Override
    public PageResult<PartnerShareProfitVO> getByPartnerPage(StoreShareProfitPageReqVO reqVO) {
        if (StringUtils.isNotBlank(reqVO.getStartMonth())) {
            LocalDate startTime = LocalDateTimeUtils.beginOfMonth(DateUtil.parseLocalDateTime(reqVO.getStartMonth() + "-01", DateUtils.FORMAT_YEAR_MONTH_DAY)).toLocalDate();
            reqVO.setStartTime(startTime);
        }

        if (StringUtils.isNotBlank(reqVO.getStartMonth())) {
            LocalDate endTime = LocalDateTimeUtils.endOfMonth(DateUtil.parseLocalDateTime(reqVO.getStartMonth() + "-01", DateUtils.FORMAT_YEAR_MONTH_DAY)).toLocalDate();
            reqVO.setEndTime(endTime);
        }
        PageResult<PartnerShareProfitVO> pageResult = new PageResult<>();
        pageResult.setList(Collections.emptyList());
        pageResult.setTotal(0l);
        Integer count = storeTradeShareProfitMapper.getByPartnerCount(reqVO);
        if (count > 0) {
            reqVO.setStart((reqVO.getPageNo()-1) * reqVO.getPageSize());
//            reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
            List<PartnerShareProfitVO> shareProfitList = storeTradeShareProfitMapper.getByPartnerPage(reqVO);
            pageResult.setList(shareProfitList);
            pageResult.setTotal(count.longValue());
        }
        return pageResult;
    }

}